const goog = {
    string: {
        //  Replace &nbsp; with white space.
        normalizeSpaces(str) {
            return str.replace(/\xa0|[ \t]+/g, ' ');
        },
    },
    dom: {
        NodeType: {
            ELEMENT: 1,
            ATTRIBUTE: 2,
            TEXT: 3,
            CDATA_SECTION: 4,
            ENTITY_REFERENCE: 5,
            ENTITY: 6,
            PROCESSING_INSTRUCTION: 7,
            COMMENT: 8,
            DOCUMENT: 9,
            DOCUMENT_TYPE: 10,
            DOCUMENT_FRAGMENT: 11,
            NOTATION: 12,
        },
        getOwnerDocument(node) {
            return node.nodeType === goog.dom.NodeType.DOCUMENT ? node : node.ownerDocument || node.document;
        },
        getDomHelper(opt_element) {
            return opt_element
                ? new goog.dom.DomHelper(goog.dom.getOwnerDocument(opt_element))
                : goog.dom.defaultDomHelper_ || (goog.dom.defaultDomHelper_ = new goog.dom.DomHelper());
        },
        canUseQuerySelector_(parent) {
            return !!(parent.querySelectorAll && parent.querySelector);
        },
        getElementsByClass(className, opt_el) {
            let parent = opt_el || document;
            if (goog.dom.canUseQuerySelector_(parent)) {
                return parent.querySelectorAll('.' + className);
            }
            return goog.dom.getElementsByTagNameAndClass_(document, '*', className, opt_el);
        },
        getElementByClass(className, opt_el) {
            let parent = opt_el || document;
            let retVal = null;
            if (parent.getElementsByClassName) {
                retVal = parent.getElementsByClassName(className)[0];
            } else if (goog.dom.canUseQuerySelector_(parent)) {
                retVal = parent.querySelector('.' + className);
            } else {
                retVal = goog.dom.getElementsByTagNameAndClass_(document, '*', className, opt_el)[0];
            }
            return retVal || null;
        },
        getElementsByTagNameAndClass(opt_tag, opt_class, opt_el) {
            return goog.dom.getElementsByTagNameAndClass_(document, opt_tag, opt_class, opt_el);
        },
        getElementsByTagNameAndClass_(doc, opt_tag, opt_class, opt_el) {
            let parent = opt_el || doc;
            let tagName = opt_tag && opt_tag != '*' ? String(opt_tag).toUpperCase() : '';

            if (goog.dom.canUseQuerySelector_(parent) && (tagName || opt_class)) {
                let query = tagName + (opt_class ? '.' + opt_class : '');
                return parent.querySelectorAll(query);
            }

            // Use the native getElementsByClassName if available, under the assumption
            // that even when the tag name is specified, there will be fewer elements to
            // filter through when going by class than by tag name
            if (opt_class && parent.getElementsByClassName) {
                let els = parent.getElementsByClassName(opt_class);

                if (tagName) {
                    let arrayLike = {};
                    let len = 0;

                    // Filter for specific tags if requested.
                    for (let i = 0, el; (el = els[i]); i++) {
                        if (tagName == el.nodeName) {
                            arrayLike[len++] = el;
                        }
                    }
                    arrayLike.length = len;

                    return arrayLike;
                } else {
                    return els;
                }
            }

            let els = parent.getElementsByTagName(tagName || '*');

            if (opt_class) {
                let arrayLike = {};
                let len = 0;
                for (let i = 0, el; (el = els[i]); i++) {
                    let className = el.className;
                    // Check if className has a split function since SVG className does not.
                    if (typeof className.split == 'function' && goog.array.contains(className.split(/\s+/), opt_class)) {
                        arrayLike[len++] = el;
                    }
                }
                arrayLike.length = len;
                return arrayLike;
            } else {
                return els;
            }
        },
        getRequiredElementByClass(className, opt_root) {
            let retValue = goog.dom.getElementByClass(className, opt_root);
            if (!retValue) {
                throw new Error('No element found with className: ' + className);
            }
            return retValue;
        },
        global: globalThis,
        DomHelper(opt_document) {
            this.document_ = opt_document || goog.global.document || document;
        },
    },
};

export const bot = {
    locators: {
        className: {
            canUseQuerySelector_(root) {
                return !!(root.querySelectorAll && root.querySelector);
            },
            single(target, root) {
                if (!target) {
                    throw new Error('No class name specified');
                }
                target = target.trim();
                if (target.indexOf(' ') !== -1) {
                    throw new Error('Compound class names not permitted');
                }

                // Closure will not properly escape class names that contain a '.' when using
                // the native selectors API, so we have to handle this ourselves.
                if (bot.locators.className.canUseQuerySelector_(root)) {
                    try {
                        return root.querySelector('.' + target.replace(/\./g, '\\.')) || null;
                    } catch (e) {
                        throw new Error('An invalid or illegal class name was specified');
                    }
                }
                const elements = goog.dom.getElementsByTagNameAndClass('*', target, root);
                return elements.length ? elements[0] : null;
            },
        },
        css() {},
        id: {
            single(target, root) {
                let dom = goog.dom.getDomHelper(root);

                let e = dom.getElement(target);
                if (!e) {
                    return null;
                }

                // On IE getting by ID returns the first match by id _or_ name.
                if (bot.dom.getAttribute(e, 'id') == target && root != e && goog.dom.contains(root, e)) {
                    return e;
                }

                let elements = dom.getElementsByTagNameAndClass('*');
                let element = goog.array.find(elements, function (element) {
                    return bot.dom.getAttribute(element, 'id') == target && root != element && goog.dom.contains(root, element);
                });
                return /**@type{Element}*/ (element);
            },
        },
        linkText() {},
        name() {},
        relative() {},
        tagName() {},
        xpath() {},

        getOnlyKey(target) {
            for (const k in target) {
                if (target.hasOwnProperty(k)) {
                    return k;
                }
            }
            return null;
        },
        findElement(target, opt_root) {
            const key = bot.locators.getOnlyKey(target);
            if (key) {
                const strategy = STRATEGIES_[key];
                if (strategy && typeof strategy.single === 'function') {
                    const root = opt_root || document;
                    return strategy.single(target[key], root);
                }
            }
        },

        partialLinkText() {},
    },
    dom: {
        findElement() {},
        getAttribute() {},
    },
    setWindow() {},
    action: {
        click(element, opt_coords, opt_mouse, opt_force) {
            const coords = bot.action.prepareToInteractWith_(element, opt_coords);
            const mouse = opt_mouse || new bot.Mouse();
            mouse.move(element, coords);
            mouse.pressButton(bot.Mouse.Button.LEFT);
            mouse.releaseButton(opt_force);
        },
        doubleClick() {},
        rightClick() {},
        prepareToInteractWith_() {},
        moveMouse() {},
        type() {},
        submit() {},
    },
    events: {
        fire() {},
    },
};

const STRATEGIES_ = {
    className: bot.locators.className,
    'class name': bot.locators.className,

    css: bot.locators.css,
    'css selector': bot.locators.css,

    relative: bot.locators.relative,

    id: bot.locators.id,

    linkText: bot.locators.linkText,
    'link text': bot.locators.linkText,

    name: bot.locators.name,

    partialLinkText: bot.locators.partialLinkText,
    'partial link text': bot.locators.partialLinkText,

    tagName: bot.locators.tagName,
    'tag name': bot.locators.tagName,

    xpath: bot.locators.xpath,
};

export const core = {
    firefox: {
        unwrap(string) {
            return string;
        },
    },
    events: {
        setValue() {},
    },
};

export default goog;
